#include <sstream>
#include "Mundo.h"

#define NOMBRE_POR_DEFECTO "NO_NAME"

using namespace TP::Juego;

Mundo::Mundo() {
	_nombre = NOMBRE_POR_DEFECTO;
	_cantidadJugadores = 0;
}

Mundo::Mundo(const string& pNombre) {
	this->_nombre = pNombre;
	this->_cantidadJugadores = 0;
}

Mundo::~Mundo() {
	for (unsigned int cur = 0; cur < _niveles.size(); cur++) {
		delete _niveles[cur];
	}
}

void Mundo::setNombre(const string& nombre) {
	_nombre = nombre;
}

const string& Mundo::getNombre() const {
	return _nombre;
}

void Mundo::setCantidadJugadores(unsigned cantidad) {
	_cantidadJugadores = cantidad;
}

unsigned Mundo::getCantidadJugadores() const {
	return _cantidadJugadores;
}

unsigned Mundo::getCantidadNiveles() const {
	return _niveles.size();
}

Nivel* Mundo::getNivel(unsigned idNivel) {
	Nivel* nivelReturn = NULL;
	int pos = obtenerPosicionNivel(idNivel);
	if (pos >= 0)
		nivelReturn = _niveles[pos];

	return nivelReturn;
}

void Mundo::crearNivel(unsigned id, unsigned anchura, unsigned altura, 
		unsigned tiempo, const std::string &rutaImagenFondo) {
	removerNivel(id);
	Nivel* nuevoNivel = new Nivel(id, anchura, altura, tiempo, rutaImagenFondo);
	if (nuevoNivel != NULL) {
		_niveles.push_back(nuevoNivel);
	}
}

void Mundo::addNivel(Nivel *nivel) {
	if (nivel != NULL) {
		removerNivel(nivel->getId());
		_niveles.push_back(nivel);
	}
}

bool Mundo::removerNivel(unsigned idNivel) {
	int pos = obtenerPosicionNivel(idNivel);
	if (pos >= 0) {
		delete _niveles[pos];
		_niveles.erase(_niveles.begin() + pos);
		return true;
	}
	return false;
}

bool Mundo::operator==(const Mundo &mundo) {
	return (_nombre == mundo._nombre);
}

bool Mundo::operator!=(const Mundo &mundo) {
	return (_nombre != mundo._nombre);
}

int Mundo::obtenerPosicionNivel(unsigned idNivel) {
	unsigned int pos = 0;
	unsigned int cantNiveles = _niveles.size();
	while ((pos < cantNiveles) && (idNivel != _niveles[pos]->getId())) {
		++pos;
	}

	if (pos < cantNiveles) {
		return pos;
	}
	return -1;
}

string Mundo::aXML() {
	TiXmlDocument* doc = new TiXmlDocument();
	TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", "");
	doc->LinkEndChild(decl);

	doc->LinkEndChild(this->aElementoXML());

	TiXmlPrinter printer;
	doc->Accept(&printer);
	return printer.CStr();
}

string Mundo::aXML(const string& pPath) {
	TiXmlDocument* doc = new TiXmlDocument(pPath.c_str());
	TiXmlDeclaration * decl = new TiXmlDeclaration("1.0", "", "");
	doc->LinkEndChild(decl);

	doc->LinkEndChild(this->aElementoXML());

	TiXmlPrinter printer;
	doc->Accept(&printer);
	return printer.CStr();
}

void Mundo::parseXML(const string& pDescriptor) {
	TiXmlDocument doc;
	doc.Parse(pDescriptor.c_str(), 0, TIXML_ENCODING_UTF8);
	this->deElementoXML(doc.FirstChildElement());
}

TiXmlElement* Mundo::aElementoXML() {
	TiXmlElement * escenarioXML = new TiXmlElement("Mundo");

	escenarioXML->SetAttribute("nombre", this->_nombre.c_str());

	std::stringstream stream;
	stream << this->_cantidadJugadores;
	escenarioXML->SetAttribute("cantidad_jugadores", stream.str().c_str());

	// Serializo la lista de actores.
	TiXmlElement * niveles = new TiXmlElement("Niveles");

	for (size_t cur = 0; cur < this->_niveles.size(); ++cur) {
		niveles->LinkEndChild(this->_niveles[cur]->aElementoXML());
	}

	escenarioXML->LinkEndChild(niveles);
	return escenarioXML;
}

void Mundo::deElementoXML(TiXmlElement* pElemento) {
	if (strcmp(pElemento->Value(), "Mundo") == 0) {
		this->_nombre = pElemento->Attribute("nombre");
		std::stringstream str(pElemento->Attribute("cantidad_jugadores"));
		str >> this->_cantidadJugadores;

		TiXmlElement * elemento = pElemento->FirstChildElement(
				)->FirstChildElement();

		while (NULL != elemento) {
			Nivel *nivel = new Nivel();
			nivel->deElementoXML(elemento);
			this->_niveles.push_back(nivel);
			elemento = elemento->NextSiblingElement();
		}
	}
}

Mundo* Mundo::crearDeXML(const string& pDescriptor) throw (Excepcion&) {
	TiXmlDocument doc;
	doc.Parse(pDescriptor.c_str(), 0, TIXML_ENCODING_UTF8);

	if (strcmp(doc.FirstChildElement()->Value(), "Mundo") != 0)
		throw Excepcion("El descriptor no corresponde a un objeto de"
				" clase Mundo.");

	Mundo* mundo = new Mundo();
	mundo->deElementoXML(doc.FirstChildElement());
	return mundo;
}
